﻿(function(window){
var 
	mooluZoom = function(){
		return new muZoom(Array.prototype
						  .slice.apply(arguments));
	},
	
	muZoom = function(arryConfig){
		this._init(arryConfig);
		this._initLoad();
	};

muZoom.prototype = {
	
	_init : function(params){
		var 
			cfg = {},
			len = params.length,
			p, param;
			
		for(var i=2; i<len; i++){
		    
			param = params[i];
			switch(typeof param){
				
				case 'number':
					
					break;
				case 'string':
					
					break;
				case 'function':
				
					break;
				case 'object':
					
					for(p in param){
						cfg[p] = param[p];
					}
			}
		}
		
		var image = params[0];
		var viewer = params[1];

		this._image = image;
		this._zoom = document.createElement('img');
		this._viewer = viewer;
		this._viewerWidth = 0;
		this._viewerHeight = 0;
		this._preload = new Image();
		this._rect = null;
		this._repairLeft = 0;
		this._repairTop = 0;
		this._rangeWidth = 0;
		this._rangeHeight = 0;
		this._timer = null;
		this._loaded = false;
		this._substitute = false;
		
		var opt = this._setOptions(cfg);
		
		this._scale = opt.scale;
		this._max = opt.max;
		this._min = opt.min;
		this._originPic = opt.originPic;
		this._zoomPic = opt.zoomPic;
		this._rangeWidth = opt.rangeWidth;
		this._rangeHeight = opt.rangeHeight;
		
		this.delay = opt.delay;
		this.autoHide = opt.autoHide;
		this.mouse = opt.mouse;
		this.rate = opt.rate;
		
		this.onLoad = opt.onLoad;
		this.onStart = opt.onStart;
		this.onMove = opt.onMove;
		this.onEnd = opt.onEnd;
		
		var oThis = this,
			END = function(){
				oThis._end();
			};
		
		this._END = function(){
			oThis._timer = setTimeout(END, oThis.delay);
		};
		this._START = $$OBEA.bind(this._start, this);
		this._MOVE = $$OBEA.bind(this._move, this);
		this._MOUSE = $$OBEA.bind(this._mouse, this);
		this._OUT = $$OBEA.bind(function(e){
			e = $$OBEA.helpe.fixEvent(e);
			if(!e.relatedTarget)
				this._END();
		}, this);
		
		$$OBEA.selfe.fireEvent(this, 'init');
	},
	
	_setOptions : function(options){
		this.options = {
			scale: 0,
			max: 10,
			min: 1.5,
			originPic: '',
			zoomPic: '',
			rangeWidth: 0,
			rangeHeight: 0,
			delay: 20,
			autoHide: true,
			mouse: false,
			rate: .2,
			onLoad: function(){},
			onStart: function(){},
			onMove: function(){},
			onEnd: function(){}
		};
		return $$OBEA.extend(this.options, options || {});
	},
	
	_initLoad : function(){
		var 
			image = this._image,
			originPic = this._originPic,
			useOrigin = !this._zoomPic && this._scale,
			loadImage = $$OBEA.bind(useOrigin ? 
				this._loadOriginImage : this._loadImage, this);
		
		this.autoHide && this._hide();
				
		if(originPic && originPic != image.src){
			image.onload = loadImage;
			image.src = originPic;
		}else if(image.src){
			if(!image.complete){
				image.onload = loadImage;
			}else{
				loadImage();
			}
		}else{
			return;
		}
		
		if(!useOrigin){
			var 
				preload = this._preload,
				zoomPic = this._zoomPic || image.src,
				loadPreload = $$OBEA.bind(this._loadPreload, this);
				
			if(zoomPic != preload.src){
				preload.onload = loadPreload;
				preload.src = zoomPic
			}else{
				if(!preload.complete){
					preload.onload = loadPreload;
				}else{
					this._loadPreload();
				}
			}
		}
	},
	
	_loadOriginImage : function(){
		this._image.onload = null;
		this._zoom.src = this._image.src;
		this._initLoaded();
	},
	
	_loadImage : function(){
		this._image.onload = null;
		if(this._loaded){
			this._initLoaded();
		}else{
			this._loaded = true;
			if(this._scale){
				this._substitute = true;
				this._zoom.src = this._image.src;
				this._initLoaded();
			}
		}
	},
	
	_loadPreload : function(){
		this._preload.onload = null;
		this._zoom.src = this._preload.src;
		if(this._loaded){
			if(!this._substitute){
				this._initLoaded();
			}
		}else{
			this._loaded = true;
		}
	},
	
	_initLoaded : function(src){
		this._initSize();
		this._initViewer();
		this._initData();
		
		$$OBEA.selfe.fireEvent(this, 'load');
		this.onLoad();
		this.start();
	},
	
	_initSize : function(){
		var 
			zoom = this._zoom,
			image = this._image,
			scale = this._scale;
		
		if(!scale){
			scale = this._preload.width / image.width;
		}
		
		this._scale = scale = 
		Math.min(Math.max(this._min, scale), this._max);
		
		zoom.width = Math.ceil(image.width * scale);
		zoom.height = Math.ceil(image.height * scale);
	},
	
	_initViewer : function(){
		var 
			zoom = this._zoom,
			viewer = this._viewer,
			styles = {
				padding : 0,
				overflow : 'hidden'
			},
			p = $$OBEA.getStyle(viewer, 'position');
		
		if(p != 'relative' && p != 'absolute'){
			styles.position = 'relative';
		}
		
		$$OBEA.setStyle(viewer, styles);
		zoom.style.position = 'absolute';
		
		if(!$$OBEA.contains(viewer, zoom)){
			viewer.appendChild(zoom);
		}
	},
	
	_initData : function(){
		var 
			zoom = this._zoom,
			image = this._image,
			viewer = this._viewer,
			scale = this._scale,
			rangeWidth = this._rangeWidth,
			rangeHeight = this._rangeHeight;
		
		this._rect = $$OBEA.rect(image);
		
		this._repairLeft = image.clientLeft + 
			parseInt($$OBEA.getStyle(image, 'padding-left'));
		this._repairTop = image.clientTop + 
			parseInt($$OBEA.getStyle(image, 'padding-top'));
			
		if(rangeWidth > 0 && rangeHeight > 0){
			rangeWidth = Math.ceil(rangeWidth);
			rangeHeight = Math.ceil(rangeHeight);
			
			this._viewerWidth = Math.ceil(rangeWidth * scale);
			this._viewerHeight = Math.ceil(rangeHeight * scale);
			
			$$OBEA.setStyle(viewer, {
				width : this._viewerWidth + 'px',
				height : this._viewerHeight + 'px'
			});
		}else{
			
			var styles;
			if(!viewer.clientWidth){
				var style = viewer.style;
				styles = {
					display : style.display,
					position : style.position,
					visibility : style.visibility
				};
				$$OBEA.setStyle(viewer, {
					display : 'block',
					position : 'absolute',
					visibility : 'hidden'
				});
			}
			
			this._viewerWidth = viewer.clientWidth;
			this._viewerHeight = viewer.clientHeight;
			
			if(styles){
				$$OBEA.setStyle(viewer, styles);
			}
			
			rangeWidth = Math.ceil(this._viewerWidth / scale);
			rangeHeight = Math.ceil(this._viewerHeight / scale);
		}
		this._rangeWidth = rangeWidth;
		this._rangeHeight = rangeHeight;
	},
	
	_start : function(){
		clearTimeout(this._timer);
		var 
			viewer = this._viewer,
			image = this._image,
			scale = this._scale;
		
		viewer.style.display = 'block';
		$$OBEA.selfe.fireEvent(this, 'start');
		
		this.onStart();
		
		$$OBEA.helpe.removeEvent(image, 'mouseover', this._START);
		$$OBEA.helpe.removeEvent(image, 'mousemove', this._START);
		$$OBEA.helpe.addEvent(document, 'mousemove', this._MOVE);
		$$OBEA.helpe.addEvent(document, 'mouseout', this._OUT);
		
		this.mouse && $$OBEA.helpe.addEvent(document,
		$$OBEA.browser.firefox ? 'DOMMouseScroll' : 'mousewheel', 
		this._MOUSE);
	},
	
	_move : function(e){
		clearTimeout(this._timer);
		var 
			e = $$OBEA.helpe.fixEvent(e),
			x = e.pageX,
			y = e.pageY,
			rect = this._rect;
		
		if(x < rect.left || x > rect.right ||
		   y < rect.top || y > rect.bottom){
		
			this._END();
		}else{
			var 
				pos = {},
				scale = this._scale,
				zoom = this._zoom,
				viewerWidth = this._viewerWidth,
				viewerHeight = this._viewerHeight;
			
			pos.left = viewerWidth / 2 - (x - rect.left - this._repairLeft) * scale;
			pos.top = viewerHeight / 2 - (y - rect.top - this._repairTop) * scale;
			
			$$OBEA.selfe.fireEvent(this, 'repair', e, pos);
			
			x = Math.ceil(Math.min(Math.max(pos.left, viewerWidth - zoom.width), 0));
			y = Math.ceil(Math.min(Math.max(pos.top, viewerHeight - zoom.height), 0));
			
			zoom.style.left = x + 'px';
			zoom.style.top = y + 'px';
			
			$$OBEA.selfe.fireEvent(this, 'move', e, x, y);
			this.onMove();
		}
		
	},
	
	_end : function(){
		$$OBEA.selfe.fireEvent(this, 'end');
		this.onEnd();
		this.autoHide && this._hide();
		this.stop();
		this.start();
	},
		
	_hide : function(){
		this._viewer.style.display = 'none';
	},
	
	_mouse : function(e){
		
		e = $$OBEA.helpe.fixEvent(e);
		
		this._scale += (e.wheelDelta ? e.wheelDelta / (-120) :
							(e.detail || 0) / 3) * this.rate;
		var opt = this.options;
		this._rangeWidth = opt.rangeWidth;
		this._rangeHeight = opt.rangeHeight;
		
		this._initSize();
		this._initData();
		this._move(e);
		e.preventDefault();
	},
	
	start : function(){
		if(this._viewerWidth && this._viewerHeight){
			var 
				image = this._image,
				START = this._START;
			
			$$OBEA.helpe.addEvent(image, 'mouseover', START);
			$$OBEA.helpe.addEvent(image, 'mousemove', START);
		}
	},
	
	stop : function(){
		clearTimeout(this._timer);
		$$OBEA.helpe.removeEvent(this._image, 'mouseover', this._START);
		$$OBEA.helpe.removeEvent(this._image, 'mousemove', this._START);
		$$OBEA.helpe.removeEvent(document, 'mousemove', this._MOVE);
		$$OBEA.helpe.removeEvent(document, 'mouseout', this._OUT);
		$$OBEA.helpe.removeEvent(document, 
			$$OBEA.browser.firefox ? 'DOMMouseScroll' : 'mousewheel', 
			this._MOUSE);
	},
	
	reset : function(options){
		this.stop();
		var 
			viewer = this._viewer,
			zoom = this._zoom;
		
		if($$OBEA.contains(viewer, zoom)){
			viewer.removeChild(zoom);
		}
		
		var opt = $$OBEA.extend(this.options, options || {});
		
		this._scale = opt.scale;
		this._max = opt.max;
		this._min = opt.min;
		this._originPic = opt.originPic;
		this._zoomPic = opt.zoomPic;
		this._rangeWidth = opt.rangeWidth;
		this._rangeHeight = opt.rangeHeight;
		
		this._loaded = this._substitute = false;
		this._rect = null;
		this._rapairLeft = this._repairTop =
		this._viewerWidth = this._viewerHeight = 0;
		
		this._initLoad();
	},
	
	destroy : function(){
		$$OBEA.selfe.fireEvent(this, 'destroy');
		this.stop();
		
		if($$OBEA.contains(this._viewer, this._zoom)){
			this._viewer.removeChild(this._zoom);
		}
		
		this._image.onload = this._preload.onload = 
		this._image = this._preload = this._zoom = 
		this._viewer = this.onLoad = this.onStart =
		this.onMove = this.onEnd = this._START =
		this._MOVE = this._END = this._OUT = null;
	}
};	

window.mooluZoom = mooluZoom;
window.muZoom = muZoom;

})(window)